package org.e_gold.service.impl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.apache.log4j.Logger;
import org.e_gold.dao.ContractDAO;
import org.e_gold.dao.SettledContractDAO;
import org.e_gold.dto.BetRule;
import org.e_gold.factory.AppProperties;
import org.e_gold.factory.Manager;
import org.e_gold.factory.export.MonitorThread;
import org.e_gold.model.Contract;
import org.e_gold.model.Event;
import org.e_gold.model.SettledContract;
import org.e_gold.service.AIService;
import org.e_gold.service.BrowserService;
import org.e_gold.service.ConfigService;
import org.e_gold.service.ContractService;
import org.e_gold.service.SettledContractService;
import org.e_gold.service.UserAccountService;
import org.e_gold.util.CalcUtil;
import org.e_gold.util.Enums.ContractTypeEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class AIServiceImpl implements AIService {

	private static final Logger logger = Logger.getLogger(AIServiceImpl.class);

	@Autowired
	private UserAccountService userAccountService;

	@Autowired
	private SettledContractDAO settledContractDAO;

	@Autowired
	private SettledContractService settledContractService;

	@Autowired
	private BrowserService browserService;

	@Autowired
	private AppProperties appProperties;

	@Autowired
	private ContractService contractService;

	@Autowired
	private ContractDAO contractDAO;

	@Autowired
	private MonitorThread monitor;

	@Autowired
	private ConfigService configService;

	private int BET_CURRENT_COUNT = 0;
	private int BET_TIMES = 0;

	@Override
	public boolean filterEvent(Event event) {

		String eventName = event.getName().toLowerCase();
		Boolean result = false;
		if (appProperties.isSafeMode()) {
			if (!eventName.contains(" women")
					&& !eventName.contains("u14")
					&& !eventName.contains("u15")
					&& !eventName.contains("u16")
					&& !eventName.contains("u17")
					&& !eventName.contains("u18")
					&& !eventName.contains("u19")
					&& !eventName.contains("u20")
					&& !eventName.contains("u21")
					&& !eventName.contains("u22")
					&& !eventName.contains("u23")
					&& !eventName.contains("u24")
					&& !eventName.contains("u25")
					&& !eventName.contains("u26")
					&& !eventName.contains("u27")
					&& !eventName.contains("u28")

					//					&& !eventName.contains("fethiyespor")
					//					&& !eventName.contains("mersin idman yurdu")
					//					&& !eventName.contains("united sc")
					//					&& !eventName.contains("shillong lajong fc")
					//					&& !eventName.contains("al yarmok")
					//					&& !eventName.contains("al tilal aden")
					//					&& !eventName.contains("young")
					//					&& !eventName.contains("youth")
					//					&& !eventName.contains("reserve")
					//
					//					// giai brahad
					//					&& !eventName.contains("riffa")
					//					&& !eventName.contains("manama")
					//					&& !eventName.contains("malkia")
					//					&& !eventName.contains("busaiteen")
					//					&& !eventName.contains("riffa")
					//					&& !eventName.contains("budaiya")
					//					&& !eventName.contains("shabbab")
					//					&& !eventName.contains("ittihad")
					//					&& !eventName.contains("qalali")
					//					&& !eventName.contains("bahrain")
					//					&& !eventName.contains("muharraq")
					//					&& !eventName.contains("hala")
					//					&& !eventName.contains("ahli")
					//					&& !eventName.contains("ittifaq")
					//					&& !eventName.contains("hadd")
					//					&& !eventName.contains("sitra")
					//					&& !eventName.contains("najma")
					//					&& !eventName.contains("isa town")
					//					&& !eventName.contains("tadhmon")
					//
					//					&& !eventName.contains("vietnam")
					//
					//					&& !eventName.contains("brisbane")
					//					&& !eventName.contains("ipswich")
					//					&& !eventName.contains("north star")
					//					&& !eventName.contains("rochedale")
					//					&& !eventName.contains("tigres")
					//					&& !eventName.contains("boreham")
					//
					//					&& !eventName.contains("forza")
					//					&& !eventName.contains("cimarron")

					) {
				result = true;
			} else {
				result = false;
			}
		} else {
			result = true;
		}

		if(result){
			if(event.getPlayTime() < appProperties.getBetMinTime() || event.getPlayTime() > appProperties.getBetMaxTime()){
				logger.info("Remove because play_time not in (" + appProperties.getBetMinTime() + " - " + appProperties.getBetMaxTime() + ")");
				return false;
			}
			if(event.getPlayTime() == 45){
				logger.info("Remove because break-time");
				return false;
			}

		}

		return result;
	}

	private List<Contract> filterListContract(List<Contract> allContracts){
		List<Contract> qualifyContracts = new ArrayList<Contract>();
		try{
			String strBetRules = configService.get_bet_rule();
			if(StringUtils.containsIgnoreCase(strBetRules, ConfigService.BET_RULE_SPLIT)){
				String[] betRules = strBetRules.split(ConfigService.BET_RULE_SPLIT);

				//looping rule
				for(String strRule : betRules){
					try{
						BetRule rule = new BetRule(strRule);

						//checking contract with specified rule
						for(Contract contract : allContracts){
							if(contract.getSection().equals(ContractTypeEnum.ALTERNATE_MATCH_GOAL.toString())){
								//							if(StringUtils.contains(contract.getContractType(), "Under")){
								Float rate = contract.getRate();
								int currentTotalGoal = contract.getEvent().getTotalGoal();
								int underTotalGoal = CalcUtil.getUnderTotalGoal(contract.getContractType());
								int playTime = contract.getEvent().getPlayTime();

								if(rule.isQualify(playTime, rate, currentTotalGoal, underTotalGoal)){
									//priority for added contract only
									if(!qualifyContracts.contains(contract)){
										contract.setQualify(true);
										qualifyContracts.add(contract);
									}
								}
							}
							//for FULL_TIME contract
							if(contract.getSection().equals(ContractTypeEnum.FULL_TIME.toString())){
								logger.info("AI got fulltime - but temporary remove");
							}
						}
					}catch(Exception ex){
						logger.error(ToStringBuilder.reflectionToString(ex, ToStringStyle.SHORT_PREFIX_STYLE));
					}
				}

				//update onetime only
				if(!CollectionUtils.isEmpty(qualifyContracts)){
					contractDAO.update(qualifyContracts);
				}
			}
		}catch(Exception ex){
			logger.info("filterListContract: " + ex.getMessage());
			logger.error(ToStringBuilder.reflectionToString(ex, ToStringStyle.SHORT_PREFIX_STYLE));

		}

		return qualifyContracts;
	}

	@Override
	public List<Contract> getQualifyContract(List<Contract> allContracts) {

		if (CollectionUtils.isNotEmpty(allContracts)) {
			// update all current contract to false qualify
			contractService.updateFalseQualifyContract(allContracts);

			// remove playing contract
			removePlacedContract(allContracts);

			// sort play time
			Collections.sort(allContracts);

			List<Contract> qualifyContracts = filterListContract(allContracts);

			// if unsafe mode sort rate from max to min
			if (!appProperties.isSafeMode()) {
				Collections.sort(qualifyContracts, new RateComparator());
			}
			logger.info("::::::::::::::::::::::::::::::::::::::::");
			logger.info("::::::::::::::::::::::::::::::::::::::::");
			logger.info("::::::End Filter Qualify Contract:::::::");
			logger.info(":: Total qualify contracts : " + qualifyContracts.size() + "  ::");
			logger.info("::::::::::::::::::::::::::::::::::::::::");
			if (qualifyContracts.size() > 0) {
				return qualifyContracts;
			}
		}
		return null;
	}

	private void removePlacedContract(List<Contract> allContracts) {
		Iterator<Contract> iterRunningContract = Manager.workingContract.iterator();

		while(iterRunningContract.hasNext()){
			Contract cRunning = iterRunningContract.next();

			if(allContracts.contains(cRunning)){
				allContracts.remove(cRunning);
			}
		}
	}

	class RateComparator implements Comparator<Contract> {

		@Override
		public int compare(Contract o1, Contract o2) {

			return o2.getRate() < o1.getRate() ? -1 : o2.getRate() == o1.getRate() ? 0 : 1;
		}

	}

	@Override
	public void virtualPlaceBet(List<Contract> qualifyContract, float bet_amount_per_time) {

		try {
			BET_CURRENT_COUNT = configService.get_bet_current_count();
			BET_TIMES = configService.get_bet_times();

			logger.info("Roll total balance: " + bet_amount_per_time);
			if (!userAccountService.is_bet_slot_full()) {
				for (Contract bestContract : qualifyContract) {

					if(is_placed_contract(bestContract)){
						logger.info("placed contract: " + bestContract.getContractType());
						continue;
					}

					String resultPlaceBet = "" ;
					resultPlaceBet = browserService.placeBet(bestContract, bet_amount_per_time); 

					if (!StringUtils.isEmpty(resultPlaceBet)) {
						BET_CURRENT_COUNT++;
						SettledContract settledContract = new SettledContract();
						settledContract.setContract(bestContract);
						fillInfo(settledContract, bestContract, bet_amount_per_time, resultPlaceBet);

						logger.info("::::::::::::::::::::::::::::::::::::::::::::");
						logger.info("@@ Virtual place bet for best event : " + bestContract.getEvent().getName());
						logger.info("@@ best contract : " + bestContract.getContractType());
						logger.info("@@ best rate : " +  bestContract.getRate());
						logger.info("@@ stackAmount : " + settledContract.getBetAmount());
						logger.info("@@ bet Return : " + settledContract.getWonAmount());
						logger.info("@@ current play time : " + bestContract.getEvent().getPlayTime());

						logger.info("@@ -------------------------");
						logger.info("@@ BET_AMOUNT_PER_TIME : " + bet_amount_per_time);
						logger.info("@@ BET_CURRENT_COUNT : " + BET_CURRENT_COUNT);
						logger.info("::::::::::::::::::::::::::::::::::::::::::::");

						settledContract.setRunning(true);
						settledContract.setUnderGoal(CalcUtil.getUnderTotalGoal(bestContract.getContractType()));
						settledContract.setBetSlip(resultPlaceBet);
						settledContract.setSettledPlayTime(bestContract.getEvent().getPlayTime());
						settledContract.setSettledTotalGoals(bestContract.getEvent().getTotalGoal());
						settledContract.setDangerous(false);
						settledContract.setCurrentTime(bestContract.getEvent().getPlayTime());

						// update current bet count
						bet_placed(settledContract);
						logger.info("@@ BET_CURRENT_COUNT : " + BET_CURRENT_COUNT);
						logger.info("::::::::::::::::::::::::::::::::::::::::::::");

						break;
					}
					if(!is_qualify_next_bet(bet_amount_per_time)){
						break;
					}
				}
			}
		} catch (Exception e) {
			logger.error(" virtualPlaceBet error");
			logger.error(e);
		}
	}

	private void fillInfo(SettledContract settledContract, Contract bestContract, float bet_amount_per_time, String resultPlaceBet){
		try{
			//filter bet_slip_result here
			//			String betContract = "";
			String stackAmount = "";
			String betToReturn = "";
			String betSlipRefs = "";
			String splitPattern = "Stake:|Bet Ref:|To Return:";
			resultPlaceBet = StringUtils.replace(resultPlaceBet, " ", " ");;
			Pattern p = Pattern.compile(splitPattern);
			String[] tokens = p.split(resultPlaceBet);
			if(tokens.length == 4){
				//				betContract = tokens[0];
				stackAmount = StringUtils.trim(tokens[1]);
				betToReturn = StringUtils.trim(tokens[2]);
				betSlipRefs = StringUtils.trim(tokens[3]);
			}
			settledContract.setBetAmount(bet_amount_per_time);
			settledContract.setWonAmount(Float.parseFloat(betToReturn));
			settledContract.setResultAmount(Float.parseFloat(stackAmount));
			settledContract.setBet_ref(betSlipRefs);
		}catch(Exception ex){
			ex.printStackTrace();

			settledContract.setBetAmount(bet_amount_per_time);
			settledContract.setWonAmount(bet_amount_per_time * bestContract.getRate());
			settledContract.setResultAmount(0F);
		}
	}

	/**
	 * <ul>
	 * 	<li>Placed contract: </li>
	 * 		<ol>
	 * 			<li>Same contract, same totalGoal</li>
	 * 			<li>Same event, totalGoal < current running</li>
	 * 		</ol>
	 * </ul>
	 * @param bettingContract
	 * @return
	 * @author Thang Nguyen
	 */
	private boolean is_placed_contract(Contract bettingContract){
		Iterator<Contract> iterContract = Manager.workingContract.iterator();

		while(iterContract.hasNext()){
			Contract runningContract = iterContract.next();
			Event runningEvent = runningContract.getEvent();

			Event bettingEvent = bettingContract.getEvent();
			//same event
			if(runningEvent.getName().equals(bettingEvent.getName())){

				//same contract: not bet
				if(runningContract.getId().longValue() == bettingContract.getId().longValue()){
					return true;
				}

				//				//better contract: bet
				//				int bettingTotal = CalcUtil.getUnderTotalGoal(bettingContract.getContractType()) ;
				//				int runningTotal = CalcUtil.getUnderTotalGoal(runningContract.getContractType()) ;

				//				if(bettingTotal < runningTotal){
				//					return true;
				//				}
			}
		}

		return false;
	}

	private void bet_placed(SettledContract sc){
		settledContractDAO.insert(sc);

		//update bet_current count
		configService.set_bet_current_count(BET_CURRENT_COUNT);

		//update balance
		Float balance = userAccountService.getBalance();
		balance -= sc.getBetAmount();
		userAccountService.setBalance(balance);

		//update manager bet playing
		Manager.bet_placed(sc);

		//update last bet amount
		configService.set_last_bet_amount(sc.getBetAmount());

		//update amount_per_time
		userAccountService.updateAmount_Per_Time();

		//update monitor
		monitor.offer(balance);
		monitor.offer(sc);
	}

	private boolean is_qualify_next_bet(float bet_amount_per_time){
		try{
			if(BET_TIMES - BET_CURRENT_COUNT >= 0){
				Float balance = userAccountService.getBalance();
				if(balance > 0f && bet_amount_per_time != 0f){
					return true;
				}
			}
		}catch(Exception ex){
			logger.info(ex.getMessage());
			logger.error(ex);
			ex.printStackTrace();
		}
		return false;
	}

	@Override
	public Boolean calcResult(SettledContract running) {

		running.setWon(running.getCurrentGoal() < running.getUnderGoal());
		running.setRunning(false);
		settledContractDAO.update(running);
		return running.getWon();
	}

}
